/***************************日期类型格式化扩展**************************/
Date.prototype.format = function (pattern) {
  pattern = pattern || "yyyy-MM-dd HH:mm:ss";
  var o = {
    "M+": this.getMonth() + 1, //月份
    "d+": this.getDate(), //日
    "h+": this.getHours() % 12 == 0 ? 12 : this.getHours() % 12, //小时
    "H+": this.getHours(), //小时
    "m+": this.getMinutes(), //分
    "s+": this.getSeconds(), //秒
    "q+": Math.floor((this.getMonth() + 3) / 3), //季度
    S: this.getMilliseconds(), //毫秒
  };
  var week = {
    0: "/u65e5",
    1: "/u4e00",
    2: "/u4e8c",
    3: "/u4e09",
    4: "/u56db",
    5: "/u4e94",
    6: "/u516d",
  };
  if (/(y+)/.test(pattern)) {
    pattern = pattern.replace(
      RegExp.$1,
      (this.getFullYear() + "").substr(4 - RegExp.$1.length)
    );
  }
  if (/(E+)/.test(pattern)) {
    pattern = pattern.replace(
      RegExp.$1,
      (RegExp.$1.length > 1
        ? RegExp.$1.length > 2
          ? "/u661f/u671f"
          : "/u5468"
        : "") + week[this.getDay() + ""]
    );
  }
  for (var k in o) {
    if (new RegExp("(" + k + ")").test(pattern)) {
      pattern = pattern.replace(
        RegExp.$1,
        RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)
      );
    }
  }
  return pattern;
};

/***************************数组类型扩展**************************/
Array.prototype.sum = function (key) {
  var total = 0;
  this.forEach(function (item) {
    if (key) {
      var val;
      if (typeof key == "function") {
        val = key(item);
      } else {
        val = item[key];
      }
      if (typeof val == "number") {
        total += val;
      }
    }
  });
  return total;
};
/**
 * 按列分组数据
 * @param {...string} groupByKeys - 分组键
 * @returns {Object} - 分组后的对象
 */
Array.prototype.groupBy = function (...groupByKeys) {
  return this.reduce((acc, item) => {
    let key = groupByKeys
      .map((k) => {
        let gk = k.split(".");
        let v = item;
        for (let i = 0; i < gk.length; i++) {
          v = v[gk[i]];
        }
        return v;
      })
      .join(","); // 生成唯一的分组键
    let accItem = acc.singleOrDefault((a) => a.key == key);
    if (!accItem) {
      accItem = { key: key, items: [] };
      acc.push(accItem);
    }
    accItem.items.push(item);
    return acc;
  }, []);
};
Array.prototype.insert = function (item, i) {
  if (typeof i != "number") return;

  if (i >= 0 && i < this.length) this.splice(i, 0, item);
  else this.push(item);
};
Array.prototype.replace = function (item, newItem) {
  for (var i = 0; i < this.length; i++) {
    if (this[i] === item) {
      this.splice(i, 1, newItem);
      break;
    }
  }
};
Array.prototype.remove = function (item) {
  for (var i = 0; i < this.length; i++) {
    if (typeof item == "function" && item(this[i])) {
      this.splice(i, 1);
      break;
    } else if (this[i] === item) {
      this.splice(i, 1);
      break;
    }
  }
};
Array.prototype.where = function (conditionCallback) {
  var arr = [];
  for (var i = 0; i < this.length; i++) {
    if (
      conditionCallback &&
      typeof conditionCallback == "function" &&
      conditionCallback(this[i])
    )
      arr.push(this[i]);
  }
  return arr;
};
Array.prototype.singleOrDefault = function (conditionCallback) {
  for (var i = 0; i < this.length; i++) {
    if (
      conditionCallback &&
      typeof conditionCallback == "function" &&
      conditionCallback(this[i])
    )
      return this[i];
  }
  return null;
};
Array.prototype.any = function (conditionCallback) {
  for (var i = 0; i < this.length; i++) {
    if (
      conditionCallback &&
      typeof conditionCallback == "function" &&
      conditionCallback(this[i])
    )
      return true;
  }
  return false;
};
Array.prototype.select = function (callback) {
  var result = [];
  for (var i = 0; i < this.length; i++) {
    if (callback && typeof callback == "function") {
      result.push(callback(this[i]));
    }
  }
  return result;
};
Array.prototype.sortBy = function (fieldName, desc) {
  if (fieldName && typeof fieldName == "string") {
    this.sort(function (a, b) {
      var x = a[fieldName].toLowerCase();
      var y = b[fieldName].toLowerCase();
      if (x < y) {
        return desc ? 1 : -1;
      }
      if (x > y) {
        return desc ? -1 : 1;
      }
      return 0;
    });
  }
};
Array.prototype.moveUp = function (index) {
  this.move(index, index - 1);
};
Array.prototype.moveDown = function (index) {
  this.move(index, index + 1);
};
Array.prototype.move = function (index, targetIndex) {

  if (this.length <= 1 || index == targetIndex) return;

  if (index > targetIndex ){
    if (targetIndex < 0 || index >= this.length)return;
  }

  if (index < targetIndex){
    if (index < 0 || targetIndex >= this.length)return;
  }
  let moving = this[index];
  let target = this[targetIndex];
  this.splice(targetIndex, 1, moving);
  this.splice(index, 1, target);
};
/***************************数字类型扩展**************************/
Number.prototype.PrefixInteger = function (length) {
  var num = this;
  return (Array(length).join("0") + num).slice(-length);
};
Number.prototype.formatNumber = function (decimals) {
  /*
   * 参数说明：
   * decimals：保留几位小数
   * dec_point：小数点符号
   * thousands_sep：千分位符号
   * */

  decimals = decimals === 0 || decimals || 2;
  let number = this,
    dec_point = ".",
    thousands_sep = ",";
  number = (number + "").replace(/[^0-9+-Ee.]/g, "");
  var n = !isFinite(+number) ? 0 : +number,
    prec = !isFinite(+decimals) ? 0 : Math.abs(decimals),
    sep = typeof thousands_sep === "undefined" ? "," : thousands_sep,
    dec = typeof dec_point === "undefined" ? "." : dec_point,
    s = "",
    toFixedFix = function (n, prec) {
      var k = Math.pow(10, prec);
      return "" + Math.floor(n * k) / k;
    };
  s = (prec ? toFixedFix(n, prec) : "" + Math.floor(n)).split(".");
  var re = /(-?\d+)(\d{3})/;
  while (re.test(s[0])) {
    s[0] = s[0].replace(re, "$1" + sep + "$2");
  }

  if ((s[1] || "").length < prec) {
    s[1] = s[1] || "";
    s[1] += new Array(prec - s[1].length + 1).join("0");
  }
  return s.join(dec);
};
Number.prototype.toFixed = function (precision) {
  var result, dot;
  if (isNaN(this)) return NaN;
  var str = this + "";
  var reg = new RegExp("\\.(\\d{" + precision + "})");
  if (/[^\-.\d]/.test(str) || !reg.test(str)) {
    result = str;
  } else {
    str = str.replace(reg, "$1.");
    result = Math.round(parseFloat(str)) / Math.pow(10, precision) + "";
  }
  /*
   * 处理进位后无小数
   */
  dot = result.indexOf(".");
  if (dot < 0) {
    result += ".";
    dot = result.indexOf(".");
  }
  /*
   * 处理多次进位
   */
  var len = result.length - (dot + 1);
  if (len < precision) {
    for (var i = 0; i < precision - len; i++) {
      result += 0;
    }
  }
  return result;
};

/***************************字符类型扩展**************************/
String.prototype.isDate = function () {
  var valid = /\/Date\((-?\d+)\)\//i.test(this);
  valid = valid || /^\d{4}-\d{2}-\d{2} \d{4}:\d{2}:\d{2}$/i.test(this);
  return valid;
};
String.prototype.toDate = function () {
  var date;
  if (/\/Date\((-?\d+)\)\//i.test(this)) {
    date = eval(this.replace(/\/Date\((-?\d+)\)\//i, "new Date($1)"));
    return date;
  } else if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/i.test(this)) {
    date = new Date(this.replace(/-/g, "/"));
    return date;
  } else if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+$/i.test(this)) {
    let str = this.replace(/T/g, " ");
    str = str.replace(/\.\d+/g, "");
    date = new Date(str.replace(/-/g, "/"));
    return date;
  } else if (/^\d{4}-\d{2}-\d{2}$/i.test(this)) {
    date = new Date(this.replace(/-/g, "/"));
    return date;
  }
  return this;
};
String.prototype.format = function (pattern) {
  var date = this.toDate();
  if (date instanceof Date) return date.format(pattern);
  else return date;
};
String.prototype.isUrl = function () {
  return /^(https?|ftp|file):\/\/[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]$/.test(
    this
  );
};
String.prototype.isNumber = function () {
  return /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(this);
};
String.prototype.toNumber = function (digits) {
  return new Number(this).toFixed(digits);
};
String.prototype.urlEncode = function () {
  return encodeURIComponent(this);
};
String.prototype.urlDecode = function () {
  return decodeURIComponent(this);
};
String.prototype.endTrim = function (s) {
  s = s || " ";
  var str = this;
  while (str.substring(str.lastIndexOf(s)) === s) {
    str = str.substring(0, str.lastIndexOf(s));
  }
  return str;
};
String.prototype.startTrim = function (s) {
  s = s || " ";
  var str = this;
  while (str.indexOf(s) == 0) {
    str = str.substring(str.indexOf(s) + s.length);
  }
  return str;
};
String.prototype.trim = function (s) {
  s = s || " ";
  return this.startTrim(s).endTrim(s);
};
String.prototype.startsWith = function (s) {
  return this.indexOf(s) === 0;
};
String.prototype.endsWith = function (s) {
  var i = this.lastIndexOf(s);
  return this.length - i === s.length;
};
String.prototype.contains = function (s) {
  return this.indexOf(s) >= 0;
};
